package com.xdf.ecommerce.filter;

import com.alibaba.fastjson.JSON;
import com.xdf.ecommerce.constant.CommonConstant;
import com.xdf.ecommerce.constant.GatewayConstant;
import com.xdf.ecommerce.utils.GatewayUtils;
import com.xdf.ecommerce.utils.TokenUtils;
import com.xdf.ecommerce.vo.JwtToken;
import com.xdf.ecommerce.vo.LoginUserInfo;
import com.xdf.ecommerce.vo.UsernameAndPassword;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author chanchaw
 * @create 2025-10-18 12:24
 */
@RequiredArgsConstructor
@Slf4j
@Component
public class GlobalLoginOrRegisterFilter implements GlobalFilter, Ordered {
    // 注册中心客户端，可以从注册中心获取服务实例的信息
    private final LoadBalancerClient loadBalancerClient;
    private final RestTemplate restTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 登录/注册，执行后退出
        Mono<Void> voidMono = doLoginOrRegister(request, response);
        if(null != voidMono) return voidMono;

        // 其他的URI，非 登录/注册
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst(CommonConstant.JWT_USER_INFO_KEY);
        LoginUserInfo loginUserInfo = null;

        try{
            loginUserInfo = TokenUtils.getUserInfo(token);
        }catch (Exception ex){
            log.error("从token解析LoginUserInfo出现异常：[{}]",ex.getMessage(),ex);
        }

        if(loginUserInfo == null){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        return chain.filter(exchange);
    }

    private Mono<Void> doLoginOrRegister(ServerHttpRequest request,ServerHttpResponse response){
        String uri = request.getURI().getPath();
        if(!GatewayUtils.isLoginOrRegister(uri)) return null;
        String url = "";
        if(uri.contains(GatewayConstant.LOGIN_URI)) url = GatewayConstant.AUTHORITY_CENTER_TOKEN_URL_FORMAT;
        if(uri.contains(GatewayConstant.REGISTER_URI)) url = GatewayConstant.AUTHORITY_CENTER_REGISTER_URL_FORMAT;
        if(url.length() == 0) return null;

        String token = getTokenFromAuthorityCenter(request, url);
        String tokenParam = null == token ? "null" : token;
        response.getHeaders().add(CommonConstant.JWT_USER_INFO_KEY, tokenParam);
        response.setStatusCode(HttpStatus.OK);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        // 必须在 GlobalCacheRequestBodyFilter 之后，要大于其 order
        return HIGHEST_PRECEDENCE + 2;
    }

    private String parseBodyFromRequest(ServerHttpRequest request) {
        Flux<DataBuffer> body = request.getBody();
        AtomicReference<String> bodyRef = new AtomicReference<>();

        // 订阅缓冲区消费请求体中的数据
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            // 一定要 release，因为在 GlobalCacheRequestBodyFilter 中
            // 使用 retain 保证数据始终保存在缓冲区中，这里使用后要释放掉
            // 否则会造成内存泄露
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });
        return bodyRef.get();
    }

    private String getTokenFromAuthorityCenter(ServerHttpRequest request, String uriFormat){
        // 通过负载均衡器获取注册中心的一个实例
        ServiceInstance serviceInstance = loadBalancerClient.choose(CommonConstant.AUTHORITY_CENTER_SERVICE_ID);
        // 打印注册中心服务ID，实例ID，实例metaInfo
        log.info("注册中心serviceId:[{}],注册中心实例ID:[{}],实例元数据:[{}]", serviceInstance.getServiceId(),serviceInstance.getInstanceId(), JSON.toJSONString(serviceInstance.getMetadata()));

        UsernameAndPassword requestBody = JSON.parseObject(parseBodyFromRequest(request), UsernameAndPassword.class);
        String requestUrl = String.format(uriFormat, serviceInstance.getHost(), serviceInstance.getPort());
        log.info("请求URL：[{}],requestBody:[{}]",requestUrl,requestBody.toString());

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        JwtToken token = restTemplate.postForObject(requestUrl, new HttpEntity<>(JSON.toJSONString(requestBody), headers), JwtToken.class);

        if(null == token) return null;
        return token.getToken();
    }
}
